using Reversi;

//
// Modified: 2009/09/28, Jeremiah Blanchard (Full Sail)
namespace FullSailAFI.GamePlaying
{
    class MinimaxBehavior : Reversi.Behavior
    {
        //
        // The agent's knowledge is key to proper use of this behavior.
        //
        private Agent agent;

        //
        // Behavior constructor
        //
        public MinimaxBehavior(Agent _agent)
        {
            agent = _agent;
        }

        //
        // This function starts the look ahead process to find the best move
        // for the current player color.
        //
        public bool run()
        {
            // Kick off the look ahead.
            agent.move = GetBestMove(agent.board, agent.color, agent.lookAheadDepth);

            return (agent.move != null);
        }

        //
        // This function uses look ahead to evaluate all valid moves for a
        // given player color and returns the best move it can find. This
        // method will only be called if there is at least one valid move
        // for the player of the designated color.
        //
        public ComputerMove GetBestMove(Board board, int color, int depth)
        {
            ComputerMove bestMove = null;
            //
            // TODO: Perform minimax for each move to determine which is best.
            //
            int turn = 0;

            // Take a look at all positions on the screen.
            for (int row = 0; row < 8; row++)
                for (int col = 0; col < 8; col++)
                {
                    ComputerMove move = new ComputerMove(row,col);
                    //
                    // If the move is valid:
                    if (board.IsValidMove(color, row, col))
                    {
                        //
                        //  - Apply the move to a copy of the board.
                        Board newBoard = new Board();
                        newBoard.Copy(board);

                        newBoard.MakeMove(color, row, col);

                        turn = -color;
                        // check that the next player can move
                        if (!newBoard.HasAnyValidMove(turn))
                        {
                            turn = -turn;
                        }
                        //  - If the end of game or depth limit is reached, set the
                        //    rank of the current move based on the Board's
                        //    evaluation.
                        if (depth <= 0 || newBoard.IsTerminalState())
                        {
                            move.rank = Evaluate(newBoard, color);
                        }
                      
                        //
                        //  - Otherwise, determine the next game move using
                        //    recursion and set the rank of the current move to that
                        //    of the next move.
                        else
                            move.rank = GetBestMove(newBoard, turn, depth - 1).rank;
                        //
                        //  - Keep track of the best move executed so far.
                        if (bestMove == null || (move.rank*color > bestMove.rank*color))
                            bestMove = move;
                      
                    }
                    //
                    //
                    // NOTE: Don't forget - if there are no valid moves for the
                    //       next player, play is returned to the original
                    //       player (forfeit recognition.)
                    //
                        //       Remember - the rank is color neutral. A negative
                        //       number is good for black, and a positive number
                        //       is good for white.
                    //
                }
            // Return the best move found.
            return bestMove;
        }

        private int Evaluate(Board board, int color)
        {
            return board.Evaluate(agent.scoreWeight, agent.forfeitWeight, color);
        }
    }
}
